#!/usr/bin/env python

import tables
import os
import sys
from datetime import date


def str2date(isoformat):
    """Converts string formated date, following ISO 8601 standard (YYYY-MM-DD)."""
    date_components = [int(i) for i in isoformat.split('-')]
    try:
        return date(*date_components)
    except ValueError, err:
        raise InvalidDateError(err)


class InvalidDateError(Exception):
    pass


class InvalidStockCode(Exception):
    pass


class SetupConfig(object):
    """Main configuration for the service"""
    def __init__(self):
        self.hdf5_file_name = 'ibov.h5'
        self.hdf5_file = tables.openFile(self.hdf5_file_name, mode='r')
        

class Arguments(object):
    """Class that represents and treats the command line arguments"""
    def __init__(self):
        self.dt_begin = None
        self.dt_end = None
        argc = len(sys.argv)
        if argc > 1:
            self.cd_stock = sys.argv[1].upper()
            if argc > 2:
                self.dt_begin = str2date(sys.argv[2])
            if argc > 3:
                self.dt_end = str2date(sys.argv[3])
                if self.dt_end < self.dt_begin:
                    raise InvalidDateError('Invalid dates error Begin Date > End Date')
        else:
            raise InvalidStockCode('No stock code given')


class StockSelect(object):
    """Class to select stocks' time series"""
    def __init__(self, cd_stock, dt_begin=None, dt_end=None):
        self.cd_stock = cd_stock
        self.dt_begin = dt_begin
        self.dt_end = dt_end
    
    def select(self, table):
        """Execute a select statement on a table"""
        if self.cd_stock and self.dt_begin and self.dt_end:
            fn_date_clause = lambda dt: dt >= self.dt_begin.isoformat() and dt <= self.dt_end.isoformat()
        elif self.cd_stock and self.dt_begin:
            fn_date_clause = lambda dt: dt >= self.dt_begin.isoformat()
        elif self.cd_stock:
            fn_date_clause = lambda dt: True
        l_data = [(row['date'], row['close']) for row in table.iterrows() if fn_date_clause(row['date'])]
        l_data.sort(cmp=lambda x, y: cmp(x[0], y[0]))
        return StockResultSet(l_data)


class StockResultSet(object):
    """Class that contains the result set of a select statement executed"""
    def __init__(self, rw_serie):
        self._rw_serie = rw_serie
        self._c_iter = -1
    
    def next(self):
        if self._c_iter < len(self._rw_serie):
            self._c_iter += 1
            return self._rw_serie[self._c_iter]
        else:
            raise StopIteration()
    
    def __iter__(self):
        """returns the iterator itself"""
        return self
    
    def __len__(self):
        """returns the size of underlying data"""
        return len(self._rw_serie)
    
    def __str__(self):
        """returns the string representation of time series data"""
        return os.linesep.join([' '.join(str(item) for item in row) for row in self._rw_serie])


def usage():
    """Usage message"""
    sys.stderr.write('''
Usage: marketdataservice.py STOCK_CODE [BEGIN DATE] [END DATE]

Examples:
    $ marketdataservice.py VALE5
    $ marketdataservice.py VALE5 2008-07-01
    $ marketdataservice.py VALE5 2008-07-01 2009-07-01
    $ marketdataservice.py VALE5 2008-07-01 2008-07-01

Wilson Freitas
wilson@aboutwilson.net
''')
    sys.exit(1)

def main():
    cfg = SetupConfig()
    try:
        args = Arguments()
    except Exception, ex:
        cfg.hdf5_file.close()
        sys.stderr.write('ERROR MESSAGE: ' + str(ex))
        usage()
    
    stmt = StockSelect(args.cd_stock, args.dt_begin, args.dt_end)
    rset = stmt.select(cfg.hdf5_file.getNode('/stocks', args.cd_stock))
    
    print rset
    
    cfg.hdf5_file.close()


if __name__ == '__main__':
    main()